export class EventEmitter<T extends Dict = {}> {
  private readonly _events: Dict<((...args: any[]) => void)[]> = {};

  on<K extends keyof T>(event: K, listener: (e: T[K]) => void): void;

  on(event: string, listener: (...args: any[]) => void): void {
    const listeners = this._events[event] || [];
    listeners.push(listener);
    this._events[event] = listeners;
  }

  off<K extends keyof T>(event: K, listener: (e: T[K]) => void): void;
  off<K extends keyof T>(event: K): void;

  off(event: string, listener?: (...args: any[]) => void): void {
    if (!listener) {
      this._events[event] = [];
    } else {
      const listeners = this._events[event];
      if (listeners) {
        const index = listeners.indexOf(listener);
        if (index >= 0) listeners.splice(index, 1);
      }
    }
  }

  emit<K extends keyof T>(event: K, e: T[K]): void;
  emit<K extends keyof T>(event: K): void;

  emit(event: string, ...args: any[]): void {
    const listeners = this._events[event];
    if (listeners) {
      for (const listener of listeners) {
        listener(...args);
      }
    }
  }
}

export function mitt<T extends Dict>() {
  return new EventEmitter<T>();
}
